﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using EventLogAnalyzer.Common;
using EventLogAnalyzer.ViewModel.XAxisInclusionStrategies;

namespace EventLogAnalyzer.ViewModel
{
    public class LogDistributionViewModel : ViewModelBase
    {
        public ObservableCollection<EventLogsGroupViewModel> EventLogsGroup { get; set;}
        public event ShowLogGroupDetailsEventHandler ShowLogGroupDetailsRequest;
        public event SelectedEventHandler Selected;
        private SortByCriteria _sortedBy = SortByCriteria.Occurences;

        public LogDistributionViewModel(IEnumerable<EventLogViewModel> filteredLogs)
        {
            Logs = filteredLogs.ToList();
            EventLogsGroup = new ObservableCollection<EventLogsGroupViewModel>();
            YAxisLabels = new ObservableCollection<string>();
            XAxisLabels = new ObservableCollection<string>();
            IsInFocus = true;
        }
        
        private RelayCommand _arrangeLogGroupsAsPerOccurancesCommand;
        public RelayCommand ArrangeLogGroupsAsPerOccurancesCommand
        {
            get
            {
                if (_arrangeLogGroupsAsPerOccurancesCommand == null)
                {
                    _arrangeLogGroupsAsPerOccurancesCommand = new RelayCommand(param => ArrangeLogGroupsAsPerOccurances());
                }
                return _arrangeLogGroupsAsPerOccurancesCommand;

            }
        }

        private void ArrangeLogGroupsAsPerOccurances()
        {
            _sortedBy = SortByCriteria.Occurences;
            CreateLogGroupsAsPerOccurrancesAndSortIt();
            RecalculateXAndYAxisLabels();
            TemporaryValues.YAxisMaxValue = EventLogsGroup.Max(g => g.LogsCount);
        }

        private void CreateLogGroupsAsPerOccurrancesAndSortIt()
        {
            EventLogsGroup.Clear();
            Logs.ForEach(log =>
                         {
                             var eventLogsGroupViewModel =
                                 EventLogsGroup.ToList().Find(g => g.InclusionStrategy.DoesLogBelongHere(log));
                             if (eventLogsGroupViewModel == null)
                             {
                                 eventLogsGroupViewModel = new EventLogsGroupViewModel
                                                           {
                                                               InclusionStrategy =
                                                                   new CommonEventIdBasedStrategy(log.CommonEventId)
                                                           };
                                 eventLogsGroupViewModel.ShowLogGroupDetailsRequest += eventLogsGroupViewModel_ShowLogGroupDetailsRequest;
                                 EventLogsGroup.Add(eventLogsGroupViewModel);
                             }
                             eventLogsGroupViewModel.AddLog(log);
                         });
            EventLogsGroup.ToList().ForEach(g => g.TotalNumOfGroups = EventLogsGroup.Count);

            var sortedList = EventLogsGroup.OrderBy(g => g.LogsCount).Reverse().ToList();
            EventLogsGroup.Clear();
            int order = 0;
            sortedList.ForEach(g => {
                                        g.XAxisOrder = order++;
                                        g.XAxisValue = order.ToString() + (order % 10 == 1 ? "st" : order % 10 == 2 ? "nd" : order % 10 == 3 ? "rd" : "th");
                                        EventLogsGroup.Add(g);
                                        }
                                );


        }

        private void RecalculateXAndYAxisLabels()
        {
            if (EventLogsGroup.Count == 0)
                return;

            YAxisLabels.Clear();
            var max = EventLogsGroup.Max(x => x.LogsCount);
            for(double i = max; i >= 0; i -= max/5.0)
            {
                if (i < 0.01)
                    i = 0;
                var s = i.ToString();
                var indexOfDot = s.IndexOf(".");
                if (indexOfDot > 0)
                    s = s.Substring(0, indexOfDot + 2);
                YAxisLabels.Add(s);
            }

            XAxisLabels.Clear();
            for (int i = 0; i < EventLogsGroup.Count; i +=5)
            {
                var xAxisValue = EventLogsGroup[i].XAxisValue;
                XAxisLabels.Add(xAxisValue);
            }
        }

        void eventLogsGroupViewModel_ShowLogGroupDetailsRequest(object sender, ShowLogGroupDetailsEventHandlerArgs args)
        {
            if (ShowLogGroupDetailsRequest != null)
                ShowLogGroupDetailsRequest(this, args);
        }

        private RelayCommand _arrangeLogGroupsAsPerTimeStampCommand;
        public RelayCommand ArrangeLogGroupsAsPerTimeStampCommand
        {
            get
            {
                if (_arrangeLogGroupsAsPerTimeStampCommand == null)
                {
                    _arrangeLogGroupsAsPerTimeStampCommand = new RelayCommand(param => ArrangeLogGroupsAsPerTimeStamp());
                }
                return _arrangeLogGroupsAsPerTimeStampCommand;

            }
        }

        private void ArrangeLogGroupsAsPerTimeStamp()
        {
            _sortedBy = SortByCriteria.TimeStamp;
            CreateLogGroupsAsPerTimeStamp();
            DistributeLogsInGroups();
            SortGroups();
            RecalculateXAndYAxisLabels();
            TemporaryValues.YAxisMaxValue = EventLogsGroup.Max(g => g.LogsCount);
        }

        private void SortGroups()
        {
            var sortedList = EventLogsGroup.OrderBy(g => g.XAxisOrder).ToList();
            EventLogsGroup.Clear();
            sortedList.ForEach(g => EventLogsGroup.Add(g));
        }

        private void CreateLogGroupsAsPerTimeStamp()
        {
            var minTimeStamp = Logs.FindAll(l=> l.TimeStamp != DateTime.MinValue).Min(l => l.TimeStamp);
            minTimeStamp = minTimeStamp.Date.Add(new TimeSpan(minTimeStamp.Hour, 0, 0));

            var maxTimeStamp = Logs.Max(l => l.TimeStamp);
            maxTimeStamp = maxTimeStamp.Date.Add(new TimeSpan(maxTimeStamp.Hour + 1, 0, 0));

            var diff = maxTimeStamp.Subtract(minTimeStamp);
            TimeSpan increment;
            DateTime startValue = minTimeStamp;
            if (diff.Days > 2)
            {
                increment = new TimeSpan(1, 0, 0, 0);
                startValue = minTimeStamp.Date;
            }
            else
                increment = new TimeSpan(1, 0, 0);
            EventLogsGroup.Clear();
            int order = 0;
            for (var i = startValue; i < maxTimeStamp.Add(increment); i = i.Add(increment))
            {
                var eventLogsGroupViewModel = new EventLogsGroupViewModel { InclusionStrategy = new TimeBasedStrategy(i, i.Add(increment)), XAxisOrder = order++, XAxisValue = i.Day + "/" + i.Month + " " + (i.Hour < 10 ? "0" :"") + i.Hour + ":" + (i.Minute < 10 ? "0" :"") + i.Minute};
                eventLogsGroupViewModel.ShowLogGroupDetailsRequest += eventLogsGroupViewModel_ShowLogGroupDetailsRequest;
                EventLogsGroup.Add(eventLogsGroupViewModel);
            }
            EventLogsGroup.ToList().ForEach(g => g.TotalNumOfGroups = order);
        }

        private void DistributeLogsInGroups()
        {
            Logs.ForEach(log =>
                         {
                             var eventLogsInTimeRangeViewModel = EventLogsGroup.ToList().Find(
                                 x => x.InclusionStrategy.DoesLogBelongHere(log));
                             eventLogsInTimeRangeViewModel.AddLog(log);
                         });
        }

        public ObservableCollection<string> YAxisLabels { get; set; }
        public ObservableCollection<string> XAxisLabels { get; set; }


        public void AddLog(EventLogViewModel log)
        {
            Logs.Add(log);
        }


        private List<EventLogViewModel> Logs { get; set; }

        private bool _isInFocus;
        public bool IsInFocus
        {
            get { return _isInFocus; }
            set
            {
                if (_isInFocus != value)
                {
                    _isInFocus = value;
                    OnPropertyChanged("IsInFocus");
                }
            }
        }

        private int _order;
        public int Order
        {
            get { return _order; }
            set
            {
                if (_order != value)
                {
                    _order = value;
                    OnPropertyChanged("Order");
                }
            }
        }

        private RelayCommand _selectCommand;
        public RelayCommand SelectCommand
        {
            get
            {
                if (_selectCommand == null)
                {
                    _selectCommand = new RelayCommand(param =>
                                                      {
                                                          IsInFocus = true;
                                                          if (Selected != null)
                                                              Selected(this, new SelectedEventHandlerArgs());
                                                      });
                }
                return _selectCommand;

            }
        }


        private string _searchText = string.Empty;
        public string SearchText
        {
            get { return _searchText; }
            set
            {
                if (_searchText != value)
                {
                    _searchText = value;
                    HighlightSearchedLogGroups(_searchText);
                    OnPropertyChanged("SearchText");
                }
            }
        }

        
        private void HighlightSearchedLogGroups(string searchText)
        {
            EventLogsGroup.ToList().ForEach(g =>
            {
                g.Logs.ToList().ForEach(log => log.ShouldHighLight = false);
                g.ShouldHighLight = false;
                g.IsNotFiltered = _searchText.Trim() == string.Empty;
            });
            if (_searchText.Trim() == string.Empty)
                return;

            var searchedLogs = Logs.Where(log => log.Data.ToUpper().Contains(_searchText.ToUpper()));
            foreach (var log in searchedLogs)
            {
                log.ShouldHighLight = true;
                EventLogsGroup.ToList().FindAll(g => g.Logs.Contains(log)).ForEach(
                    g => g.ShouldHighLight = true);
            }

            if (_sortedBy == SortByCriteria.SearchText)
                ArrangeLogGroupsAsPerSearchCriteria();
        }

        private RelayCommand _arrangeLogGroupsAsPerSearchCriteriaCommand;
        public RelayCommand ArrangeLogGroupsAsPerSearchCriteriaCommand
        {
            get
            {
                if (_arrangeLogGroupsAsPerSearchCriteriaCommand == null)
                {
                    _arrangeLogGroupsAsPerSearchCriteriaCommand = new RelayCommand(param => ArrangeLogGroupsAsPerSearchCriteria());
                }
                return _arrangeLogGroupsAsPerSearchCriteriaCommand;

            }
        }

        private void ArrangeLogGroupsAsPerSearchCriteria()
        {
            _sortedBy = SortByCriteria.SearchText;
            CreateLogGroupsAsPerSearchCriteria();
            RecalculateXAndYAxisLabels();
            TemporaryValues.YAxisMaxValue = EventLogsGroup.Max(g => g.LogsCount);
        }

        private void CreateLogGroupsAsPerSearchCriteria()
        {
            EventLogsGroup.Clear();
            var matchingGroup = new EventLogsGroupViewModel{ InclusionStrategy = new SearchBasedStrategy() };
            matchingGroup.ShowLogGroupDetailsRequest += eventLogsGroupViewModel_ShowLogGroupDetailsRequest;
            EventLogsGroup.Add(matchingGroup);

            var nonMatchingGroup = new EventLogsGroupViewModel{ InclusionStrategy = new AcceptAllStrategy() };
            nonMatchingGroup.ShowLogGroupDetailsRequest += eventLogsGroupViewModel_ShowLogGroupDetailsRequest;
            EventLogsGroup.Add(nonMatchingGroup);

            Logs.ForEach(log =>
            {
                if(matchingGroup.InclusionStrategy.DoesLogBelongHere(log))
                    matchingGroup.AddLog(log);
                else
                    nonMatchingGroup.AddLog(log);
            });

            matchingGroup.TotalNumOfGroups = EventLogsGroup.Count;
            matchingGroup.XAxisOrder = 0;
            matchingGroup.XAxisValue = _searchText;
            matchingGroup.ShouldHighLight = true;
            matchingGroup.IsNotFiltered = false;

            nonMatchingGroup.TotalNumOfGroups = EventLogsGroup.Count;
            nonMatchingGroup.XAxisOrder = 1;
            nonMatchingGroup.XAxisValue = "<No Search Match>";
            nonMatchingGroup.ShouldHighLight = false;
            nonMatchingGroup.IsNotFiltered = false;
        }


    }
}